package net.sf.calibur.pool;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ObjectPool {
	private PooledObjectFactory factory;
	int maxSleeping;

	private Lock lock;
	private Map<Object, ActiveObjectTrace> idleQ;
	private Map<Object, ActiveObjectTrace> activeQ;

	public ObjectPool(PooledObjectFactory factory, int maxSleeping) {
		this.factory = factory;
		this.maxSleeping = maxSleeping;

		lock = new ReentrantLock();
		idleQ = new HashMap<Object, ActiveObjectTrace>();
		activeQ = new HashMap<Object, ActiveObjectTrace>();
	}

	public Object getKey() {
		return factory.getKey();
	}
	
	public void getActiveObjectTrace(List<ActiveObjectTrace> list) {
		lock.lock();
		try {
			list.addAll(activeQ.values());
		} finally {
			lock.unlock();
		}
	}
	
	public int getNumIdle() {
		return idleQ.size();
	}

	public int getNumActive() {
		return activeQ.size();
	}

	public Object borrowObject() throws Exception {
		lock.lock();
		try {
			Object obj = null;
			ActiveObjectTrace trace = null;
			
			if (idleQ.isEmpty()) {
				obj = factory.makeObject();
				trace = new ActiveObjectTrace();
			} else {
				Iterator<?> itr = idleQ.keySet().iterator();
				while (itr.hasNext()) {
					obj = itr.next();
					break;
				}
				trace = idleQ.remove(obj);
			}

			factory.activateObject(obj);
			trace.touch(obj);
			activeQ.put(obj, trace);
			return obj;
		} finally {
			lock.unlock();
		}
	}

	public void returnObject(Object obj) throws Exception {
		lock.lock();
		try {
			ActiveObjectTrace trace = activeQ.remove(obj);
			if (trace == null) {
				throw new IllegalArgumentException(
						"returned object isn't created by pool. " + obj);
			}

			if (idleQ.size() > maxSleeping) {
				factory.destroyObject(obj);
			} else {
				factory.passivateObject(obj);
				trace.clear();
				idleQ.put(obj, trace);
			}
		} finally {
			lock.unlock();
		}
	}
}
